/**************************************************************************
OmegaT - Computer Assisted Translation (CAT) tool
with fuzzy matching, translation memory, keyword search,
glossaries, and translation leveraging into updated projects.
Copyright (C) 2014 Alex Buloichik
Home page: http://www.omegat.org/
Support center: http://groups.yahoo.com/group/OmegaT/
This file is part of OmegaT.
OmegaT is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OmegaT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/
package org.omegat.gui.dialogs;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Stream;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import org.omegat.util.OStrings;
import org.omegat.util.StringUtil;
import org.omegat.util.gui.DelegatingComboBoxRenderer;
import org.omegat.util.gui.StaticUIUtils;
import org.omegat.util.gui.TableColumnSizer;
import gen.core.project.RepositoryDefinition;
import gen.core.project.RepositoryMapping;
/**
* Controller for repositories mapping UI.
*
* @author Alex Buloichik (alex73mail@gmail.com)
*/
public class RepositoriesMappingController {
/**
* Enum of supported repository types corresponding to
* org.omegat.core.team2.impl.*RemoteRepository*
*/
enum RepoType {
GIT, SVN, HTTP;
public String getLocalizedString() {
return OStrings.getString("RMD_TABLE_REPO_TYPE_" + name());
}
}
private List<RepositoryDefinition> result;
private RepositoriesMappingDialog dialog;
private AbstractTableModel modelRepo;
private List<RowRepo> listRepo;
private AbstractTableModel modelMapping;
private List<RowMapping> listMapping;
private TableColumnSizer repoSizer;
private TableColumnSizer mappingSizer;
public List<RepositoryDefinition> show(Frame parent, List<RepositoryDefinition> input) {
dialog = new RepositoriesMappingDialog(parent, true);
dialog.setLocationRelativeTo(parent);
dialog.getRootPane().setDefaultButton(dialog.okButton);
StaticUIUtils.setEscapeClosable(dialog);
listRepo = new ArrayList<RowRepo>();
listMapping = new ArrayList<RowMapping>();
putData(input);
initTableModels();
initButtons();
reinitRepoUrlDropdown();
dialog.tableRepositories.getSelectionModel().addListSelectionListener(e -> updateState());
dialog.tableMapping.getSelectionModel().addListSelectionListener(e -> updateState());
dialog.tableRepositories.getModel().addTableModelListener(e -> updateState());
repoSizer = TableColumnSizer.autoSize(dialog.tableRepositories, 1, true);
mappingSizer = TableColumnSizer.autoSize(dialog.tableMapping, 0, true);
updateState();
dialog.setVisible(true);
return result;
}
private void updateState() {
dialog.btnRepoRemove.setEnabled(dialog.tableRepositories.getSelectedRow() != -1);
dialog.btnMappingRemove.setEnabled(dialog.tableMapping.getSelectedRow() != -1);
dialog.btnMappingAdd.setEnabled(dialog.tableRepositories.getRowCount() > 0);
}
@SuppressWarnings("serial")
void initTableModels() {
modelRepo = new AbstractTableModel() {
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
RowRepo r = listRepo.get(rowIndex);
switch (columnIndex) {
case 0:
return r.type == null ? null : r.type.getLocalizedString();
case 1:
return r.url;
}
return null;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
RowRepo r = listRepo.get(rowIndex);
switch (columnIndex) {
case 0:
r.type = (RepoType) aValue;
break;
case 1:
String old = r.url;
r.url = (String) aValue;
changeRepoUrl(old, r.url);
reinitRepoUrlDropdown();
break;
}
repoSizer.reset();
repoSizer.adjustTableColumns();
}
@Override
public int getRowCount() {
return listRepo.size();
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
}
@Override
public String getColumnName(int column) {
switch (column) {
case 0:
return OStrings.getString("RMD_TABLE_REPOSITORIES_TYPE");
case 1:
return OStrings.getString("RMD_TABLE_REPOSITORIES_URL");
}
return null;
}
};
dialog.tableRepositories.setModel(modelRepo);
JComboBox<RepoType> comboBox = new JComboBox<>(RepoType.values());
comboBox.setRenderer(new DelegatingComboBoxRenderer<RepoType, String>() {
@Override
protected String getDisplayText(RepoType value) {
return value == null ? "" : value.getLocalizedString();
}
});
dialog.tableRepositories.getColumnModel().getColumn(0).setCellEditor(new DefaultCellEditor(comboBox));
modelMapping = new AbstractTableModel() {
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
RowMapping r = listMapping.get(rowIndex);
switch (columnIndex) {
case 0:
return r.repoUrl;
case 1:
return r.local;
case 2:
return r.remote;
case 3:
return r.excludes;
case 4:
return r.includes;
}
return null;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
RowMapping r = listMapping.get(rowIndex);
switch (columnIndex) {
case 0:
r.repoUrl = (String) aValue;
break;
case 1:
r.local = (String) aValue;
break;
case 2:
r.remote = (String) aValue;
break;
case 3:
r.excludes = (String) aValue;
break;
case 4:
r.includes = (String) aValue;
break;
}
mappingSizer.reset();
mappingSizer.adjustTableColumns();
}
@Override
public int getRowCount() {
return listMapping.size();
}
@Override
public int getColumnCount() {
return 5;
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
}
@Override
public String getColumnName(int column) {
switch (column) {
case 0:
return OStrings.getString("RMD_TABLE_MAPPING_REPO");
case 1:
return OStrings.getString("RMD_TABLE_MAPPING_LOCAL");
case 2:
return OStrings.getString("RMD_TABLE_MAPPING_REMOTE");
case 3:
return OStrings.getString("RMD_TABLE_MAPPING_EXCLUDES");
case 4:
return OStrings.getString("RMD_TABLE_MAPPING_INCLUDES");
}
return null;
}
};
dialog.tableMapping.setModel(modelMapping);
}
void initButtons() {
dialog.cancelButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dialog.dispose();
}
});
dialog.okButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
boolean doneEditing = Stream.of(dialog.tableMapping, dialog.tableRepositories)
.map(JTable::getCellEditor).allMatch(editor -> editor == null || editor.stopCellEditing());
if (!doneEditing) {
return;
}
String r = isValid();
if (r != null) {
JOptionPane.showMessageDialog(dialog, r, OStrings.getString("TF_ERROR"),
JOptionPane.ERROR_MESSAGE);
return;
}
result = getData();
dialog.dispose();
}
});
dialog.btnRepoAdd.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
listRepo.add(new RowRepo());
int row = listRepo.size() - 1;
modelRepo.fireTableRowsInserted(row, row);
dialog.tableRepositories.setRowSelectionInterval(row, row);
}
});
dialog.btnRepoRemove.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int r = dialog.tableRepositories.getSelectedRow();
if (r >= 0) {
listRepo.remove(r);
modelRepo.fireTableRowsDeleted(r, r);
}
}
});
dialog.btnMappingAdd.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
RowMapping mapping = new RowMapping();
if (dialog.tableRepositories.getRowCount() == 1) {
mapping.repoUrl = (String) dialog.tableRepositories.getValueAt(0, 1);
}
listMapping.add(mapping);
int row = listMapping.size() - 1;
modelMapping.fireTableRowsInserted(row, row);
dialog.tableMapping.setRowSelectionInterval(row, row);
}
});
dialog.btnMappingRemove.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int r = dialog.tableMapping.getSelectedRow();
if (r >= 0) {
listMapping.remove(r);
modelMapping.fireTableRowsDeleted(r, r);
}
}
});
}
/**
* User changed repo URL - need to change in all mappings.
*/
void changeRepoUrl(String oldUrl, String newUrl) {
if (oldUrl == null) {
return;
}
for (int i = 0; i < listMapping.size(); i++) {
if (oldUrl.equals(listMapping.get(i).repoUrl)) {
listMapping.get(i).repoUrl = newUrl;
modelMapping.fireTableCellUpdated(i, 0);
}
}
}
void reinitRepoUrlDropdown() {
JComboBox<String> comboBox = new JComboBox<>();
for (RowRepo r : listRepo) {
comboBox.addItem(r.url);
}
dialog.tableMapping.getColumnModel().getColumn(0).setCellEditor(new DefaultCellEditor(comboBox));
}
/**
* Check if data is valid. Returns null if all valid.
*/
String isValid() {
Set<String> urls = new TreeSet<String>();
for (RowRepo r : listRepo) {
if (StringUtil.isEmpty(r.url)) {
return OStrings.getString("RMD_INVALID_BLANK_REPO");
}
if (r.type == null) {
return MessageFormat.format(OStrings.getString("RMD_INVALID_REPO_TYPE"), r.type);
}
if (!urls.add(r.url)) {
return MessageFormat.format(OStrings.getString("RMD_INVALID_DUPLICATE_REPO"), r.url);
}
}
for (RowMapping r : listMapping) {
if (StringUtil.isEmpty(r.repoUrl)) {
return OStrings.getString("RMD_INVALID_BLANK_REPO");
}
if (!urls.contains(r.repoUrl)) {
return MessageFormat.format(OStrings.getString("RMD_INVALID_UNKNOWN_REPO"), r.repoUrl);
}
}
return null;
}
void putData(List<RepositoryDefinition> data) {
if (data == null) {
return;
}
for (RepositoryDefinition rd : data) {
RowRepo r = new RowRepo();
r.type = RepoType.valueOf(rd.getType().toUpperCase(Locale.ENGLISH));
r.url = rd.getUrl();
listRepo.add(r);
for (RepositoryMapping rm : rd.getMapping()) {
RowMapping m = new RowMapping();
m.repoUrl = rd.getUrl();
m.local = normalizeMapping(rm.getLocal());
m.remote = normalizeMapping(rm.getRepository());
m.excludes = String.join(";", rm.getExcludes());
m.includes = String.join(";", rm.getIncludes());
listMapping.add(m);
}
}
}
List<RepositoryDefinition> getData() {
List<RepositoryDefinition> result = new ArrayList<RepositoryDefinition>();
for (RowRepo r : listRepo) {
RepositoryDefinition rd = new RepositoryDefinition();
rd.setType(r.type.name().toLowerCase(Locale.ENGLISH));
rd.setUrl(r.url);
result.add(rd);
for (RowMapping m : listMapping) {
if (r.url.equals(m.repoUrl)) {
RepositoryMapping rm = new RepositoryMapping();
rm.setLocal(normalizeMapping(m.local));
rm.setRepository(normalizeMapping(m.remote));
if (!StringUtil.isEmpty(m.excludes)) {
rm.getExcludes().addAll(Arrays.asList(m.excludes.trim().split(";")));
}
if (!StringUtil.isEmpty(m.includes)) {
rm.getIncludes().addAll(Arrays.asList(m.includes.trim().split(";")));
}
rd.getMapping().add(rm);
}
}
}
return result;
}
String normalizeMapping(String mapping) {
return StringUtil.isEmpty(mapping) ? "/" : mapping;
}
static class RowRepo {
public RepoType type;
public String url;
}
static class RowMapping {
public String repoUrl;
public String local = "/";
public String remote = "/";
public String excludes;
public String includes;
}
}